package com.garbuz.svnadmin.model;

import com.garbuz.svnadmin.data.Group;
import com.garbuz.svnadmin.data.Permission;
import com.garbuz.svnadmin.data.Repo;
import com.garbuz.svnadmin.data.User;
import com.google.common.base.Splitter;
import common.utils.FileUtil;
import org.ini4j.Ini;
import org.ini4j.Profile;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.core.wc.admin.SVNLookClient;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SvnModelReader
{
	private SvnModel model;
	private SVNLookClient svnClient;

	public SvnModel read(String repoPath, String passwordPath, String authzPath) throws Exception
	{
		svnClient = new SVNLookClient(
				SVNWCUtil.createDefaultAuthenticationManager(),
				SVNWCUtil.createDefaultOptions(true));

		model = new SvnModel();
		readStorageFolder(repoPath);
		readPasswordsFile(passwordPath);
		readAuthFile(authzPath);
		return model;
	}

	private void readAuthFile(String path) throws IOException
	{
		Ini authFile = new Ini(new FileReader(path));
		readGroups(authFile);
		readPermissions(authFile);
	}

	private void readPermissions(Ini file)
	{
		Set<Map.Entry<String, Profile.Section>> entries = file.entrySet();
		for (Map.Entry<String, Profile.Section> entry : entries)
		{
			String sectionName = entry.getKey();
			if (sectionName.endsWith(":/"))
				readRepoPermissions(sectionName, entry.getValue());
		}
	}

	private void readRepoPermissions(String sectionName, Profile.Section section)
	{
		String repoName = sectionName.substring(0, sectionName.length() - 2);
		Repo repo = model.getRepo(repoName);
		
		if (repo == null)
			return;

		for (Map.Entry<String, String> items : section.entrySet())
		{
			readPermission(repo, items);
		}
	}

	private void readPermission(Repo repo, Map.Entry<String, String> entry)
	{
		String permissionName = entry.getKey();
		String permissionValue = entry.getValue();

		Permission permission = null;

		if (permissionName.startsWith("@"))
		{
			String groupName = permissionName.substring(1);
			if (model.groupExists(groupName))
			{
				permission = new Permission();
				permission.name = groupName;
				permission.isGroup = true;
			}
		}
		else
		{
			if (model.userExists(permissionName))
			{
				permission = new Permission();
				permission.name = permissionName;
				permission.isGroup = false;
			}
		}

		if (permission != null)
		{
			permission.readonly = "r".equals(permissionValue);
			repo.permissions.add(permission);
		}
	}

	private void readGroups(Ini authFile)
	{
		Profile.Section section = authFile.get("groups");

		if (section == null)
			return;

		for (Map.Entry<String, String> entry : section.entrySet())
		{
			readGroup(entry);
		}
	}

	private void readGroup(Map.Entry<String, String> entry)
	{
		String groupName = entry.getKey();
		String usersString = entry.getValue();

		if (Group.ALL_NAME.equals(groupName))
			return;

		model.getGroups().add(new Group(groupName));

		if (usersString != null)
		{
			Iterable<String> userNames = Splitter.on(",").trimResults().split(usersString);
			for (String userName : userNames)
			{
				User user = model.getUser(userName);
				if (user != null)
					user.groupNames.add(groupName);
			}
		}
	}

	private void readPasswordsFile(String path) throws Exception
	{
		List<String> rows = FileUtil.fileToStringList(path);

		for (String row : rows)
		{
			String[] tokens = row.split(":\\{SHA}");

			User user = new User();
			user.name = tokens[0];
			user.hash = tokens.length == 2 ? tokens[1] : "";
			model.getUsers().add(user);
		}
	}

	private void readStorageFolder(String path)
	{
		File[] files = new File(path).listFiles();

		if (files == null)
			return;

		for (File file : files)
		{
			if (file.isDirectory())
			{
				Repo repo = new Repo();
				repo.name = file.getName();
				repo.date = new Date(file.lastModified());

				try
				{
					SVNLogEntry info = svnClient.doGetInfo(file, SVNRevision.HEAD);
					repo.lastCommitAuthor = info.getAuthor();
					repo.lastCommitDate = info.getDate();
				}
				catch (SVNException e)
				{
					e.printStackTrace();
				}

				model.getRepos().add(repo);
			}
		}
	}
}
